React 的动画过渡库 - React-Transition-group

在开发中实现动画的方法有很多,不管是 react 还是 vue 都有开源的动画组件库来更加方便的动画实现效果

react 动画组件库是 React-Transition-group,在说这个组件库之前,我们先来看下借助 CSS3 实现最基本的动画样式

CSS3 实现动画样式

constructor(props) {
  super(props)
  this.state = {
    show: true
  }
  this.handleToggle = this.handleToggle.bind(this)
}

render() {
  return (
    <Fragment>
      <div className={this.state.show ? 'show' : 'hide'}>hello</div>
      <button onClick={this.handleToggle}>toggle</button>
    </Fragment>
  );
}

handleToggle() {
  this.setState({
    show: this.state.show ? false : true
  })
}
.show {
  opacity: 1;
  transition: all 1s ease-in;
}

.hide {
  opacity: 0;
  transition: all 1s ease-in;
}

这样在浏览器中通过控制 toggle,就可以看到 hello 若隐若现了,就借助了 CSS3 实现了最基本的动画样式

使用 react-transition-group 实现动画

在 github 中搜索 react-transition-group ,可以看到 star 最多的项目,就是 react-transition-group

先安装依赖 npm install react-transition-group --save

然后在项目中引入 import { CSSTransition } from 'react-transition-group'

可以看到官网的demo

classNames="fade" applies fade-enter, fade-enter-active, fade-enter-done, fade-exit, fade-exit-active, fade-exit-done, fade-appear, and fade-appear-active.

Transition

过渡组件 ( Transiton ) 允许您用一个简单的声明性 API 描述随着时间的推移从一个组件状态到另一个组件状态的转换

默认展示组件某个特定状态的样式,而不是创建渐变动画

<Transition
  in={this.state.show}
  timeout={1000}
  // unmountOnExit
>
  {state=>{
    if(state === 'entering' || state === 'entered')
      return <div className="on">{state}</div>
    else 
      return <div className="off">{state}</div>
  }}
</Transition>
<button onClick={this.handleAddItem}>toggle {`${this.state.show}`}</button>

Transition 中间传入一个函数,也就是属性 children ,并获得一个参数 state,

state 包含了内部组件的 transition 状态,分别有

  • entering
  • entered
  • exiting
  • exited
  • unmounted

上述 🌰 的意思可以理解为

你设置的时间是 1000ms,当 in 从 false 变成 true 的时候, 显示 <div className="on">{state}</div>enteringentered 的状态 ), 相反的,当 in 从 true 变成 false 的时候,1000ms 之后, 切换到 <div className="off">{state}</div>exitingexited 的状态 )

运行原理

从头来看,它其实就是一个状态机,跟动画没什么关系,所以它的名字也是叫 Transition

有点像路由,在不同的组件中选择一个渲染,唯一的区别是,它只有4个路由选项:

进入 ( in === true )时,url 是 entering,1000ms 后,url 变成 entered

退出 ( in ===false ) 时,url 是 exiting,1000ms 后,url 变成 exited

然后加上 css ease-in-out 就成了动画

CSSTransition

展示组件从状态到另一个状态的动态变化,需要定义 className 和相关样式

最常用的是用来动画一个组件的安装和卸载,但也可以用来描述在适当的过渡状态

可以将我们之前用 CSS3 写的样式修改为

render() {
  return (
    <Fragment>
      <CSSTransition
        in={this.state.show}
        timeout={1000}
        // 前缀名注意S
        classNames='fade'
      >
        <div>hello</div>
      </CSSTransition>
      <button onClick={this.handleToggle}>toggle</button>  
    </Fragment>
  )
}

handleToggle() {
  this.setState({
    show: this.state.show ? false : true
  })
}

.fade-enter {
  opacity: 0;
}

.fade-enter-active {
  opacity: 1;
  transition: opacity 1s ease-in;
}

/* 入场动画执行完毕后,保持状态 */
.fade-enter-done {
  opacity: 1;
}

.fade-exit {
  opacity: 1;
}

.fade-exit-active {
  opacity: 0;
  transition: opacity 1s ease-in;
}

.fade-exit-done {
  opacity: 0;
}

这样就可以实现和之前相同的动画效果了

咋一看虽然稍微复杂了点,但是它可以带给我们很多新的特效

比如参数 intrue or false,代表了是’淡入’状态,还是’淡出’状态

timeout 代表了整个的持续时间

unmountOnExit 属性,添加到代码中,会发现当我们点隐藏的时候,对应的 DOM 被移出了,点显示的时候,DOM 又出来了

借助 react-transition-group 这个库,实现起来非常简单

继续看它的文档,这个库提供了很多钩子函数

image

假设当这个 hello 显示出来之后,希望它的颜色能变成红色,现在实现就变得很简单了

只需要在入场动画结束之后,将 color 变成 red

.fade-enter-done {
  opacity: 1;
  color: red
}

还可以用 js 的方式来实现,怎么做呢?

CSSTransition 组件中添加一个钩子onEntered

<CSSTransition
  in={this.state.show}
  timeout={1000}
  classNames='fade'
  unmountOnExit
  onEntered={(el) => {el.style.color='blue'}}
>
  <div>hello</div>
</CSSTransition>

钩子和生命周期函数是一个东西,就是在某个时刻会自动执行的函数

onEntered 钩子什么时候会自动执行呢?就是当入场动画结束之后,就会被执行

el 就是指的内部的 div 元素

如果希望第一次展示的时候也有动画效果,应该怎么办呢?

同样也需要在 CSSTransition 组件中添加一个 appear={true} ,同时在入场动画的第一帧添加 fade-appear ,同时在入场动画的第二帧以及整个过程中添加 fade-appear-active

/* enter是入场前的刹那(点击按钮),appear指页面第一次加载前的一刹那(自动) */
.fade-enter, .fade-appear {
  opacity: 0;
}

/* enter-active指入场后到入场结束的过程,appear-active则是页面第一次加载自动执行 */
.fade-enter-active, .fade-appear-active {
  opacity: 1;
  transition: opacity 1s ease-in;
}

这些也就是 react-transition-group 这个库比较核心的内容,其他更复杂的方法可以查阅 Transition

TransitionGroup

如果要做多个元素的动画切换呢?

这个时候就要用到 TransitionGroup

TransitionGroup 实际上就是实现多个Transition 或者CSSTransition组合的效果

用来管理一些列组件的动画,例如列表

首先引入这个组件 import TransitionGroup from 'react-transition-group'

constructor(props) {
  super(props)
  this.state = {
    data: []
  }
  this.handleAddItem = this.handleAddItem.bind(this)
}

render() {
  return (
    <Fragment>
      <TransitionGroup>
      {
        this.state.data.map((item, index) => {
          return (
            <CSSTransition
              timeout={1000}
              classNames='fade'
              unmountOnExit
              onEntered={(el) => {el.style.color='blue'}}
              appear={true}
              key={index}
            >
              <div>{item}</div>
            </CSSTransition>
          )
        })
      }
      </TransitionGroup>
        <button onClick={this.handleAddItem}>toggle</button>
    </Fragment>
  )
}

handleAddItem() {
  this.setState((prevState) => {
    return {
      data: [...prevState.data, 'item']
    }
  })
}

这样配合 TransitionGroupCSSTransition 就可以进行多个元素或者组件切换这样的动画效果了

完整代码

transition 动画